#include "message.h"

QByteArray Message::encode_msg_base (MessageType msg_type, uint length) {
	QByteArray base(sizeof(MessageType), '\0');
	*((MessageType*)base.data()) = msg_type;
	base.append((char*)&length, sizeof(length));
	return base;
}

QByteArray Message::encode_msg_field (const QByteArray &data) {
	uint length = data.length();
	QByteArray field(sizeof(uint), '\0');
	*((uint*)field.data()) = length;
	return field.append(field);
}

QByteArray Message::encode_tree_model_msg (const RemoteTreeModel &rtm) {
	QByteArray msg = Message::encode_msg_base(TREE_MODEL, sizeof(MessageType)+sizeof(uint));
	return msg;
}

QList<QByteArray> Message::decode_msg_fields (const QByteArray &data) {
	QList<QByteArray> flds;
	uint i = 0, fld_length;
	for (; i < (uint)data.length(); i += fld_length) {
		fld_length = *(uint*)(data.data()+i);
		flds.push_back(data.mid(i+sizeof(uint), fld_length-sizeof(uint)));
	}
	Q_ASSERT (i == data.length());
	return flds;
}

QByteArray Message::encode_file_info_req_msg (const QString &rel_path) {
	return encode_msg_base(FILE_INFO_REQ, sizeof(MessageType)+sizeof(uint)+rel_path.length()).append(rel_path.toUtf8());
}

QByteArray Message::encode_file_cont_req_msg (const QString &rel_path) {
	return encode_msg_base(FILE_CONT_REQ, sizeof(MessageType)+sizeof(uint)+rel_path.length()).append(rel_path.toUtf8());
}

QByteArray Message::encode_tree_model_req_msg () {
	static QByteArray msg = encode_msg_base(TREE_MODEL_REQ, sizeof(MessageType)+sizeof(uint));
	return msg;
}

QByteArray Message::encode_disconnect_msg () {
	static QByteArray msg = encode_msg_base(DISCONNECT, sizeof(MessageType)+sizeof(uint));
	return msg;
}

QByteArray Message::encode_file_info_msg (const FileInfo &file_info) {

	QByteArray msg = encode_msg_base(FILE_INFO, sizeof(MessageType)+sizeof(uint)+
												sizeof(file_info.last_modified)+
												sizeof(file_info.md5)+
												sizeof(file_info.rel_path)),
			last_modified_ba = QByteArray(sizeof(file_info.last_modified), '\0');
	*((time_t*)last_modified_ba.data()) = file_info.last_modified;
	QByteArray rel_path_fld = encode_msg_field(file_info.rel_path.toAscii()),
			last_modified_fld = encode_msg_field(last_modified_ba),
			md5_fld = encode_msg_field(file_info.md5);
	return encode_msg_base(FILE_INFO, sizeof(MessageType)+sizeof(uint) + sizeof(last_modified_fld)+
								  sizeof(md5_fld) + sizeof(rel_path_fld))
			.append(rel_path_fld).append(last_modified_fld).append(md5_fld);
}

QByteArray Message::encode_file_cont_msg (const QByteArray &file_cont) {
	return encode_msg_base(FILE_CONT, sizeof(MessageType)+sizeof(uint)+file_cont.length()).append(file_cont);
}

QByteArray Message::encode_req_reject_msg () {
	static QByteArray msg = encode_msg_base(REQ_REJECT, sizeof(MessageType)+sizeof(uint));
	return msg;
}

MessageType Message::encoded_msg_type (const QByteArray &encoded) {
	if (encoded.length() < sizeof(MessageType)+sizeof(uint))
		throw my_exception("The length of encoded message cannot be less than 5");
	return *((MessageType*)encoded.data());
}

bool Message::check_msg_length (const QByteArray &encoded) {
	return encoded.length() == *((uint*)encoded.mid(sizeof(MessageType), 4).data());
}

QByteArray Message::encoded_msg_body (const QByteArray &encoded) {
	if (check_msg_length(encoded))
		return encoded.mid(sizeof(MessageType)+sizeof(uint));
	else
		throw my_exception("The length of the encoded message is invalid");
}

FileInfo Message::decode_file_info_msg (const QByteArray &encoded) {
	QList<QByteArray> fields = decode_msg_fields(Message::encoded_msg_body(encoded));
	return FileInfo(QString::fromAscii(fields[0].data()), *((time_t*)fields[1].data()), fields[2]);
}

RemoteTreeModel* Message::decode_tree_model_msg (const QByteArray &encoded) {

	return new RemoteTreeModel();
}

void message_test0 () {
	qDebug("message_test0...");
	const char *hw = "/hello/world", *asdf = "asdfasdfasdf";
	FileInfo file_info (hw, 100, QByteArray(asdf));
	QFileSystemModel fsm;
	fsm.setRootPath("/");
	RemoteTreeModel *rtm = 0;// RemoteTreeModel::construct_from_QFileSystemModel(fsm);
	QByteArray file_info_req_msg = Message::encode_file_info_req_msg(hw),
			file_cont_req_msg = Message::encode_file_cont_req_msg(hw),
			tree_model_req_msg = Message::encode_tree_model_req_msg(),
			disconnect_msg = Message::encode_disconnect_msg(),
			file_info_msg = Message::encode_file_info_msg(file_info),
			file_cont_msg = Message::encode_file_cont_msg(QByteArray(hw)),
			req_reject_msg = Message::encode_req_reject_msg(),
			tree_model_msg = Message::encode_tree_model_msg(*rtm);
	Q_ASSERT (Message::encoded_msg_type(file_info_req_msg) == FILE_INFO_REQ);
	qDebug() << "hello";
	Q_ASSERT (Message::encoded_msg_type(file_cont_req_msg) == FILE_CONT_REQ);
	qDebug() << "hello";
	Q_ASSERT (Message::encoded_msg_type(tree_model_req_msg) == TREE_MODEL_REQ);
	qDebug() << "hello";
	Q_ASSERT (Message::encoded_msg_type(disconnect_msg) == DISCONNECT);
	qDebug() << "hello";
	Q_ASSERT (Message::encoded_msg_type(file_info_msg) == FILE_INFO);
	qDebug() << "hello";
	Q_ASSERT (Message::encoded_msg_type(file_cont_msg) == FILE_CONT);
	qDebug() << "hello";
	Q_ASSERT (Message::encoded_msg_type(req_reject_msg) == REQ_REJECT);
	qDebug() << "hello";
	Q_ASSERT (Message::encoded_msg_type(tree_model_msg) == TREE_MODEL);
	qDebug() << "hello";

	Q_ASSERT (Message::encoded_msg_body(file_info_req_msg) == QByteArray());
	qDebug() << "hello";
	Q_ASSERT (Message::encoded_msg_body(file_cont_req_msg) == QByteArray());
	qDebug() << "hello";
	Q_ASSERT (Message::encoded_msg_body(tree_model_req_msg) == QByteArray());
	qDebug() << "hello";
	Q_ASSERT (Message::encoded_msg_body(disconnect_msg) == QByteArray());
	qDebug() << "hello";
	Q_ASSERT (Message::decode_file_info_msg(file_info_msg) == file_info);
	qDebug() << "hello";
	Q_ASSERT (Message::encoded_msg_body(file_cont_msg) == QByteArray(hw));
	qDebug() << "hello";
	Q_ASSERT (Message::encoded_msg_body(req_reject_msg) == QByteArray());
	qDebug() << "hello";
	//Q_ASSERT (*Message::decode_tree_model_msg(tree_model_msg) == *rtm);

	delete rtm;
	qDebug() << "passed";
}
